package http;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import net.htmlparser.jericho.Attribute;
import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.HTMLElementName;
import net.htmlparser.jericho.Segment;
import net.htmlparser.jericho.Source;

public class Parser {
	private XmlHelperElement pathToElements = null;
	private String absoluteUrl = null;
	private Pattern linkCondition = null;
	private String titleElement = null;
	private int titleIndex = 0;
	private String titleValue = "#content#";
	private String linkElement = null;
	private int linkIndex = 0;
	private String linkValue = "#content#";
	private String captchaElement = null;
	private int captchaIndex = 0;
	private String captchaValue = "#content#";
	private boolean gotParserInfo = false;
	private XmlHelperElement pathToCaptcha;
	private XmlHelperElement pathToCaptchaForm;
	private String captchaInputTarget;
	private int captchaFormIndex;
	private boolean debug = false;
	
	public boolean validate(URL url,Source source,String page) {
		source = repair(source,page);
		System.out.println("Validating "+page);
		return validateSource(url,source,ParserConfig.getInstance().getValidatingInfo(page));
	}
	public boolean validateSource(URL url,Source source,List<XmlHelperElement> conditions) {
		boolean valid = true;
		for (XmlHelperElement e:conditions) {
			if (e.name.toLowerCase().compareTo("anyattributecontent") == 0) {
				boolean tempValid = false;
				for (Element e2:source.getAllElements(e.atts.get("tag"))) {
					if (e2.getAttributeValue(e.atts.get("attribute")) != null 
							&& e2.getAttributeValue(e.atts.get("attribute")).compareTo(e.content) == 0) {
						tempValid = true;
					}
				}
				if (tempValid == false) valid = false;
			}
			if (e.name.toLowerCase().compareTo("anyattributecontentregex") == 0) {
				boolean tempValid = false;
				for (Element e2:source.getAllElements(e.atts.get("tag"))) {
					System.out.println("Regex: "+e.content);
					System.out.println("Matching: "+e2.getAttributeValue(e.atts.get("attribute")));
					Pattern regExp = Pattern.compile(e.content);
					Matcher catIdCatcher = regExp.matcher(e2.getAttributeValue(e.atts.get("attribute")));
					if (catIdCatcher.matches()) {
						tempValid = true;
						System.out.println("AnyAttributeContentRegex - OK");
					}
				}
				if (tempValid == false) {
					valid = false;
					System.out.println("AnyAttributeContentRegex - fail");
				}
			}
			if (e.name.toLowerCase().compareTo("noattributecontentregex") == 0) {
				boolean tempValid = true;
				for (Element e2:source.getAllElements(e.atts.get("tag"))) {
					System.out.println("Regex: "+e.content);
					System.out.println("Matching: "+e2.getAttributeValue(e.atts.get("attribute")));
					Pattern regExp = Pattern.compile(e.content);
					Matcher catIdCatcher = regExp.matcher(e2.getAttributeValue(e.atts.get("attribute")));
					if (catIdCatcher.matches()) {
						tempValid = false;
						System.out.println("NoAttributeContentRegex - fail");
					}
				}
				if (tempValid == false) valid = false;
				else {
					System.out.println("NoAttributeContentRegex - OK");
				}
			}
			if (e.name.toLowerCase().compareTo("urlregex") == 0) {
				Pattern regExp = Pattern.compile(e.content);
				if (!regExp.matcher(url.getProtocol().concat("://").concat(url.getHost())
						.concat(url.getFile())).matches()) {
					valid = false;
					System.out.println("UrlRegex - fail");
				}
				else
					System.out.println("UrlRegex - OK");
			}
		}
		return valid;
	}
	private Source repair(Source source,String page) {
		List<XmlHelperElement> repairs = ParserConfig.getInstance().getRepairInfo(page);
		if (repairs.size() == 0) return source;
		String newSource = source.toString();
		for (XmlHelperElement repair:repairs) {
			if (repair.name == "remove") {
				newSource = newSource.replace(repair.content, "");
			}
			if (repair.name == "replace") {
				String[] regexReplace = repair.content.split(repair.atts.get("splitter"));
				newSource = newSource.replaceAll(regexReplace[0], regexReplace[1]);
			}
		}
		return new Source(newSource);
	}
	private void collectElementsWalkTree(XmlHelperElement rootTo,Segment rootIs,List<Segment> sList) {
		XmlHelperElement e = rootTo;
		Segment node = rootIs;
		if (e.children.size() == 1) {
			if (debug) {
				System.out.println("------------------------------------------------------");
				System.out.println("<"+rootTo.name+">");
				System.out.println("<"+rootTo.children.get(0).name.toLowerCase()+">");
				System.out.println("<"+HTMLElementName.HTML+">");
				System.out.println(rootIs.toString());
			}
			e = e.children.get(0);
			for (Element e2:node.getAllElements(rootTo.children.get(0).name.toLowerCase())) {
				boolean ok = true;
				if (debug) {
					System.out.println("###############################################");
					System.out.println("<"+e.name+">");
					System.out.println(e2.toString().substring(0, 20));
				}
				for (Entry<String,String> entry:e.atts.entrySet()) {
					if (e2.getAttributeValue(entry.getKey().toLowerCase()) != null 
						&& e2.getAttributeValue(entry.getKey().toLowerCase()).compareToIgnoreCase(entry.getValue()) != 0) {
						ok = false;
					}
				}
				for (Attribute attr:e2.getAttributes()) {
					if (e.atts.get(attr.getKey()) != null 
						&& e.atts.get(attr.getKey()).compareToIgnoreCase(attr.getValue()) != 0) {
						ok = false;
					}
				}
				if (ok) {
					collectElementsWalkTree(e, e2, sList);
					ok = true;
				}
			}
		}
		else {
			sList.add(node);
		}
	}
	private void getParserInfo(String page) {
		gotParserInfo = true;
		for (XmlHelperElement e:ParserConfig.getInstance().getParsingInfo(page)) {
			if (e.name.toLowerCase().compareTo("pathtoelements".toLowerCase()) == 0) {
				pathToElements = e;
			}
			if (e.name.toLowerCase().compareTo("titleelement".toLowerCase()) == 0) {
				titleElement = e.content;
				if (e.atts.get("index") != null) {
					try {
						titleIndex = Integer.parseInt(e.atts.get("index"));
					} catch (NumberFormatException Exception) {
						titleIndex = 0;
					}
				}
				if (e.atts.get("value") != null) {
					titleValue = e.atts.get("value");
				}
			}
			if (e.name.toLowerCase().compareTo("linkelement".toLowerCase()) == 0) {
				linkElement = e.content;
				if (e.atts.get("index") != null) {
					try {
						linkIndex = Integer.parseInt(e.atts.get("index"));
					} catch (NumberFormatException Exception) {
						linkIndex = 0;
					}
				}
				if (e.atts.get("value") != null) {
					linkValue = e.atts.get("value");
				}
			}
			if (e.name.toLowerCase().compareTo("urlforrelative".toLowerCase()) == 0) {
				absoluteUrl = e.content;
			}
			if (e.name.toLowerCase().compareTo("linkconditionregex".toLowerCase()) == 0) {
				System.out.println(e.content);
				linkCondition = Pattern.compile(e.content);
			}
			if (e.name.toLowerCase().compareTo("pathtocaptcha".toLowerCase()) == 0) {
				pathToCaptcha = e;
			}
			if (e.name.toLowerCase().compareTo("captchaelement".toLowerCase()) == 0) {
				captchaElement = e.content;
				if (e.atts.get("index") != null) {
					try {
						captchaIndex = Integer.parseInt(e.atts.get("index"));
					} catch (NumberFormatException Exception) {
						captchaIndex = 0;
					}
				}
				if (e.atts.get("value") != null) {
					captchaValue = e.atts.get("value");
				}
			}
			if (e.name.toLowerCase().compareTo("pathtocaptchaform".toLowerCase()) == 0) {
				pathToCaptchaForm = e;
				if (e.atts.get("index") != null) {
					try {
						captchaFormIndex = Integer.parseInt(e.atts.get("index"));
					} catch (NumberFormatException Exception) {
						captchaFormIndex = 0;
					}
				}
			}
			if (e.name.toLowerCase().compareTo("captchainputtarget".toLowerCase()) == 0) {
				captchaInputTarget = e.content;
			}
		}
	}
	protected void getLinksAndTitle(Source source,Map<String,Link> links,String page) {
		if (!gotParserInfo)
			getParserInfo(page);
		source = repair(source,page);

		if (pathToElements != null) {
			List<Segment> aList = new ArrayList<Segment>();
			collectElementsWalkTree(pathToElements, source, aList);
			System.out.println(aList.size());
			
			for (Segment a:aList) {
				if (titleElement != null && linkElement != null) {
					if (a.getAllElements(titleElement).size() == 0 || a.getAllElements(linkElement).size() == 0) continue;
					Element title = a.getAllElements(titleElement).get(titleIndex);
					Element link = a.getAllElements(linkElement).get(linkIndex);
					String titleString = null;
					String linkString = null;
					if (titleValue.compareTo("#content#") == 0)
						titleString = title.getContent().toString();
					else
						titleString = title.getAttributeValue(titleValue);
					if (linkValue.compareTo("#content#") == 0)
						linkString = link.getContent().toString();
					else
						linkString = link.getAttributeValue(linkValue);
					links.put(titleString, new Link(linkString));
				}
				else {
					for (Element e:a.getAllElements(HTMLElementName.A))
						links.put(e.getContent().toString(), new Link(e.getAttributeValue("href")));
				}
			}
			List<String> toRemove = new ArrayList<String>();
		    for (Entry<String,Link> pairs:links.entrySet()) {
		    	pairs.getValue().setAbsoluteUrl(absoluteUrl);
		    	System.out.println(pairs.getValue()+" - "+pairs.getKey());
		        if (linkCondition != null && !linkCondition.matcher(pairs.getValue().toString()).matches())
		        	toRemove.add(pairs.getKey());
		    }
		    for (String s:toRemove) {
		    	System.out.println(s);
		    	links.remove(s);
		    }
		}
	}
	protected Link getCaptchaImageLink(Source source,String page) {
		if (!gotParserInfo)
			getParserInfo(page);
		source = repair(source,page);
		
		Link captchaPath = null;
		if (pathToCaptcha != null) {
			List<Segment> cList = new ArrayList<Segment>();
			collectElementsWalkTree(pathToCaptcha,source,cList);
			for (Segment c:cList) {
				if (captchaElement != null) {
					Element captcha = c.getAllElements(captchaElement).get(captchaIndex);
					if (captchaValue.compareTo("#content#") == 0)
						captchaPath = new Link(captcha.getContent().toString());
					else
						captchaPath = new Link(captcha.getAttributeValue(captchaValue));
				}
				else {
					for (Element e:c.getAllElements(HTMLElementName.IMG))
						captchaPath = new Link(e.getAttributeValue("src"));
				}
			}
		}
    	captchaPath.setAbsoluteUrl(absoluteUrl);
		return captchaPath;
	}
	protected CaptchaForm getCaptchaForm(Source source,String page) {
		if (!gotParserInfo)
			getParserInfo(page);
		source = repair(source,page);
		
		CaptchaForm captchaForm = new CaptchaForm();
		if (pathToCaptchaForm != null) {
			List<Segment> cfList = new ArrayList<Segment>();
			collectElementsWalkTree(pathToCaptchaForm,source,cfList);
			Link action = new Link(cfList.get(0).getAllElements(HTMLElementName.FORM).get(captchaFormIndex).getAttributeValue("action"));
			action.setAbsoluteUrl(absoluteUrl);
			captchaForm.setAction(action);
			String method = cfList.get(0).getAllElements(HTMLElementName.FORM).get(captchaFormIndex).getAttributeValue("method");
			if (method.toLowerCase().compareTo("post") == 0)
				captchaForm.setMethod(CaptchaForm.POST);
			else
				captchaForm.setMethod(CaptchaForm.GET);
			for (Element e:cfList.get(0).getAllElements(HTMLElementName.FORM).get(captchaFormIndex).getAllElements(HTMLElementName.INPUT)) {
				if (e.getAttributeValue("type").toLowerCase().compareTo("hidden") == 0)
					captchaForm.addParameter(e.getAttributeValue("name"), e.getAttributeValue("value"));
				if (e.getAttributeValue("type").toLowerCase().compareTo("text") == 0)
					captchaForm.addParameter(e.getAttributeValue("name"));
			}
		}
		captchaForm.setCaptchaParameter(captchaInputTarget);
		return captchaForm;
	}
}